﻿using System;
using System.Collections.Generic;

namespace TLang.Parsers
{
    using Ast;

    public class PreParser
    {
        private readonly String file;
        private readonly Lexer lexer;


        public PreParser(string text, string file)
        {
            if (string.IsNullOrEmpty(file))
            {
                this.file = null;
            }
            else
            {
                this.file = file;
            }
            this.lexer = new Lexer(text, file);
        }

        /**
         * Get next node from token stream
         */
        private Node NextNode()
        {
            return NextNode(0);
        }


        /**
         * Helper for nextNode, which does the real work
         *
         * @return a Node or null if file ends
         */
        private Node NextNode(int depth)
        {
            Node first = lexer.NextToken();

            // end of file
            if (first == null)
            {
                return null;
            }

            if (Delimeter.IsOpen(first))
            {   // try to get matched (...)
                List<Node> elements = new List<Node>();
                Node next;
                for (next = NextNode(depth + 1);
                     !Delimeter.Match(first, next);
                     next = NextNode(depth + 1))
                {
                    if (next == null)
                    {
                        throw new ParserException("unclosed delimeter till end of file: " + first.ToString(), first);
                    }
                    else if (Delimeter.IsClose(next))
                    {
                        throw new ParserException("unmatched closing delimeter: " +
                                next.ToString() + " does not close " + first.ToString(), next);
                    }
                    else
                    {
                        elements.Add(next);
                    }
                }
                return new Tuple(elements, first, next, first.file, first.start, next.end, first.line, first.col);
            }
            else if (depth == 0 && Delimeter.IsClose(first))
            {
                throw new ParserException("unmatched closing delimeter: " + first.ToString() +
                        " does not close any open delimeter", first);
            }
            else
            {
                return first;
            }
        }


        /**
         * Parse file into a Node
         *
         * @return a Tuple containing the file's parse tree
         */
        public Node Parse()
        {
            List<Node> elements = new List<Node>();
            elements.Add(Name.GenName(Constants.SEQ_KEYWORD));      // synthetic block keyword

            Node s = NextNode();
            Node first = s;
            Node last = null;
            for (; s != null; last = s, s = NextNode())
            {
                elements.Add(s);
            }

            return new Tuple(
                    elements: elements,
                    open: Name.GenName(Constants.PAREN_BEGIN),
                    close: Name.GenName(Constants.PAREN_END),
                    file: file,
                    start: first == null ? 0 : first.start,
                    end: last == null ? 0 : last.end,
                    line: 0,
                    col: 0
            );
        }
    }
}
